#include "random_hyperplane_classifier.h"

#include "libsvm_classifier.h"

#include <math.h>
#include <fstream>

void random_hyperplane_classifier::train(boosting_problem & prob, int argc, char ** argv) {
	int candidates = atoi(argv[0]);
	argc --;
	argv ++;

	int l = prob.l;

	boosting_node ** xp = new boosting_node *[l];
	boosting_node ** x = new boosting_node *[l];
	for(int i = 0; i < l; i++) {
		xp[i] = new boosting_node[2];
		xp[i][0].index = 1;
		xp[i][1].index = -1;
		xp[i][1].value = 0;

		x[i] = new boosting_node[2];
		x[i][0].index = 1;
		x[i][1].index = -1;
		x[i][1].value = 0;
	}

	best_problem = new boosting_problem();
	best_problem->l = l;
	best_problem->dim = 1;
	best_problem->x = xp;
	best_problem->y = prob.y;

	boosting_problem * subproblem = new boosting_problem();
	subproblem->l = l;
	subproblem->dim = 1;
	subproblem->x = x;
	subproblem->y = prob.y;

	double * result = new double[l];

	double best_score = -1;
	for(int i = 0; i < candidates; i++) {
		vector<double> w;
		get_random_direction(w, prob.dim);
		for(int j = 0; j < l; j++) {
			subproblem->x[j][0].value = kernel(w, prob.x[j]);
		}
		
		resample_classifier * sub_classifier = new libsvm_classifier();
		sub_classifier->train(*subproblem, argc, argv);

		for(int j = 0; j < l; j++) {
			double p = sub_classifier->test(subproblem->x[j]);
			result[j] = p;
		}

		double score = get_score(subproblem->y, result, l);
		if(score > best_score) {
			best_score = score;
			swap(best_problem, subproblem);
			swap(best_classifier, sub_classifier);
			best_w = w;
		}

		delete sub_classifier;
	}

	delete [] result;

	for(int i = 0; i < subproblem->l; i++) {
		delete [] subproblem->x[i];
	}
	delete [] subproblem->x;
	delete subproblem;
}

double random_hyperplane_classifier::test(boosting_node * x) {
	boosting_node x1[2];
	x1[0].index = 1;
	x1[0].value = kernel(best_w, x);
	x1[1].index = -1;
//	x1[2].index = 0;

	return best_classifier->test(x1);
}

void random_hyperplane_classifier::load(string filename) {
	string sub_file = filename + ".sub.txt";
	best_classifier = new libsvm_classifier();
	best_classifier->load(sub_file);

	ifstream fin(filename.c_str());
	int dim;
	fin >> dim;
	best_w.resize(dim);
	for(int i = 0; i < dim; i++) {
		fin >> best_w[i];
	}
	fin.close();
}

void random_hyperplane_classifier::dump(string filename) {
	string sub_file = filename + ".sub.txt";
	best_classifier->dump(sub_file);

	ofstream fout(filename.c_str());
	fout << best_w.size() << endl;
	for(unsigned i = 0; i < best_w.size(); i++) {
		fout << " " << best_w[i];
	}
	fout.close();
}

random_hyperplane_classifier::random_hyperplane_classifier() {
	best_problem = NULL;
	best_classifier = NULL;
}

random_hyperplane_classifier::~random_hyperplane_classifier() {
	if(best_problem != NULL) {
		for(int i = 0; i < best_problem->l; i++) {
			delete [] best_problem->x[i];
		}
		delete [] best_problem->x;
		delete best_problem;
	}

	if(best_classifier != NULL) {
		delete best_classifier;
	}
}

void random_hyperplane_classifier::get_random_direction(vector<double> & w, int dim) {
	w.resize(dim);
	for(int i = 0; i < dim; i++) {
		w[i] = 2 * (rand()/(double)RAND_MAX) - 1;
	}

	// normalize
	double dis = 0;
	for(unsigned i = 0; i < w.size(); i++) {
		double v = w[i];
		dis += v * v;
	}

	dis = pow(dis, 0.5);
	for(unsigned i = 0; i < w.size(); i++) {
		w[i] /= dis;
	}
}

double random_hyperplane_classifier::kernel(vector<double> & w, boosting_node * x) {
	double sum = 0;
	int dim = (int)w.size();
	while(x->index != -1) {
		if(x->index <= dim) {
			sum += x->value * w[x->index - 1];
		}
		x++;
	}
	return sum;
}

double random_hyperplane_classifier::get_score(double * y, double * r, int l) {
	int sum = 0;
	for(int i = 0; i < l; i++) {
		int h = r[i] > 0.5 ? 1 : -1;
		if(y[i] == h) {
			sum += 1;
		}
	}
	return sum / (double) l;
}
